#ifndef _M_LOGGER_H
#define _M_LOGGER_H
#include <iostream>
#include <vector>
#include "sink.hpp"
#include <stdarg.h>
#include <atomic>
#include "looper.hpp"
#include <unordered_map>
namespace tqblog
{
    class logger
    {
    public:
        using ptr = std::shared_ptr<logger>;
        logger(std::string &loggername, std::vector<LogSink::ptr> &sink, tqblog::Formatter formatter, LogLevel::value level)
            : _loggername(loggername), _sink(sink.begin(), sink.end()), _formatter(formatter), _level(level)
        {}
        void debug(std::string file, size_t line, const char *fmt, ...)
        {
            if(shouldlog(tqblog::LogLevel::value::DEBUG))return;
            va_list pa;
            va_start(pa, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, pa);
            va_end(pa);
            tqblog::LogMsg msg(tqblog::LogLevel::value::DEBUG, line, file, _loggername, res);
            free(res);
            std::stringstream ss;
            _formatter.format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
        }
        void info(std::string file, size_t line, const char *fmt, ...)
        {
            if(shouldlog(tqblog::LogLevel::value::INFO))return;
            va_list pa;
            va_start(pa, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, pa);
            va_end(pa);
            tqblog::LogMsg msg(tqblog::LogLevel::value::INFO, line, file, _loggername, res);
            free(res);
            std::stringstream ss;
            _formatter.format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
        }
        void warn(std::string file, size_t line, const char *fmt, ...)
        {
            if(shouldlog(tqblog::LogLevel::value::WARN))return;
            va_list pa;
            va_start(pa, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, pa);
            va_end(pa);
            tqblog::LogMsg msg(tqblog::LogLevel::value::WARN, line, file, _loggername, res);
            free(res);
            std::stringstream ss;
            _formatter.format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
        }
        void error(std::string file, size_t line, const char *fmt, ...)
        {
            if(shouldlog(tqblog::LogLevel::value::ERROR))return;
            va_list pa;
            va_start(pa, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, pa);
            va_end(pa);
            tqblog::LogMsg msg(tqblog::LogLevel::value::ERROR, line, file, _loggername, res);
            free(res);
            std::stringstream ss;
            _formatter.format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
        }
        void fatal(std::string file, size_t line, const char *fmt, ...)
        {
            if(shouldlog(tqblog::LogLevel::value::FATAL))return;
            va_list pa;
            va_start(pa, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, pa);
            va_end(pa);
            tqblog::LogMsg msg(tqblog::LogLevel::value::FATAL, line, file, _loggername, res);
            free(res);
            std::stringstream ss;
            _formatter.format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
        }
        std::string getname(){
            return _loggername;
        }
        bool shouldlog(tqblog::LogLevel::value va){return _level>va;}
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::string _loggername;
        std::vector<LogSink::ptr> _sink;
        std::atomic<LogLevel::value> _level;
        tqblog::Formatter _formatter;
        std::mutex _mutex;
    };
    class SyncLogger : public logger
    {
    public:
        SyncLogger(std::string &loggername, std::vector<LogSink::ptr> &sink, tqblog::Formatter formatter, LogLevel::value level)
            : logger(loggername, sink, formatter, level)
        {
        }
        virtual void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sink.empty())
            {
                return;
            }
            for (auto &sink : _sink)
            {
                sink->log(data, len);
            }
        }
    };
    class AsyncLogger : public logger
    {
    public:
        AsyncLogger(std::string &loggername, std::vector<LogSink::ptr> &sink, tqblog::Formatter formatter, LogLevel::value level)
            : logger(loggername, sink, formatter, level), _looper(std::bind(&AsyncLogger::reallog, this, std::placeholders::_1))
        {
        }
        virtual void log(const char *data, size_t len)
        {
            _looper.push(data, len);
            // std::cout<<"ss_size"<<len<<std::endl;
        }
        void reallog(Buffer &buffer)
        {
            if (_sink.empty())
            {
                return;
            }
            for (auto &sink : _sink)
            {
                sink->log(buffer.begin(), buffer.readAbleSize());
            }
        }

    private:
        AsyncLooper _looper;
    };
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC,

    };
    class Loggerbuilder
    {
    public:
        using ptr = std::shared_ptr<Loggerbuilder>;
        Loggerbuilder() : _formatter(Formatter()), _buildLoggername("测试日志器"), _buildLoggerlevel(LogLevel::value::DEBUG), _loggertype(LoggerType::LOGGER_SYNC)
        {
        }

    public:
        void buildLoggername(std::string name)
        {
            _buildLoggername = name;
        }
        void buildLoggerlevel(LogLevel::value level)
        {
            _buildLoggerlevel = level;
        }
        void buildloggertype(LoggerType type)
        {
            _loggertype = type;
        }
        void buildloggerformatter(std::string &pattern)
        {
            _formatter = Formatter(pattern);
            _formatter.parsepattern();
        }
        template <typename sinktype, typename... Args>
        void buildsink(Args &&...args)
        {
            tqblog::LogSink::ptr sink = tqblog::SinkFactory::create<sinktype>(std::forward<Args>(args)...);
            _sink.push_back(sink);
        }
        virtual tqblog::logger::ptr build() = 0;

    protected:
        std::string _buildLoggername;
        LogLevel::value _buildLoggerlevel;
        Formatter _formatter;
        std::vector<LogSink::ptr> _sink;
        LoggerType _loggertype;
    };
    class Localloggerbuilder : public Loggerbuilder
    {
    public:
        using ptr = std::shared_ptr<Localloggerbuilder>;
        tqblog::logger::ptr build() override
        {
            if (_loggertype == LoggerType::LOGGER_SYNC)
            {
                tqblog::logger::ptr ll = tqblog::logger::ptr(new tqblog::SyncLogger(_buildLoggername, _sink, _formatter, _buildLoggerlevel));
                return ll;
            }
            else if (_loggertype == LoggerType::LOGGER_ASYNC)
            {
                tqblog::logger::ptr ll = tqblog::logger::ptr(new tqblog::AsyncLogger(_buildLoggername, _sink, _formatter, _buildLoggerlevel));
                return ll;
            }
        }
    };
    class Loggermanager
    {
    private:
        Loggermanager()
        {
            tqblog::Loggerbuilder::ptr l = tqblog::Loggerbuilder::ptr(new tqblog::Localloggerbuilder());
            l->buildLoggerlevel(tqblog::LogLevel::value::DEBUG);
            l->buildLoggername("root");
            l->buildloggertype(tqblog::LoggerType::LOGGER_SYNC);
            std::string pattern = "[%c]%f %l %m%n"; 
            l->buildloggerformatter(pattern);
            l->buildsink<tqblog::StdoutSink>();
            _root = l->build();
            _v.insert(std::make_pair("root",_root));
        }

    public:
    static tqblog::Loggermanager& getinstance(){
        static Loggermanager l;
        //c++11静态变量是线程安全的
         return l;
    }
    bool haslogger(std::string name){
        std::unique_lock<std::mutex>(_mtx);
        std::unordered_map<std::string, tqblog::logger::ptr>::iterator pos=_v.find(name);
        if(pos==_v.end())return false;
        return true;
    }
    void addlogger(tqblog::logger::ptr logger){
        std::unique_lock<std::mutex>(_mtx);
        if(haslogger(logger->getname())) return;
        _v.insert(std::make_pair(logger->getname(),logger));
    }
    tqblog::logger::ptr getlogger(std::string name){
        std::unique_lock<std::mutex>(_mtx);
        for(auto i:_v){
            if(i.first==name)return i.second;
        }
        return tqblog::logger::ptr(nullptr);

    }
    tqblog::logger::ptr getrootlogger(){
        std::unique_lock<std::mutex>(_mtx);
        return _root;
    }
    private:
        std::mutex _mtx;
        std::unordered_map<std::string, tqblog::logger::ptr> _v;
        tqblog::logger::ptr _root;
    };
    class globalloggerbuilder : public Loggerbuilder
    {
    public:
        using ptr = std::shared_ptr<Localloggerbuilder>;
        tqblog::logger::ptr build() override
        {
            tqblog::logger::ptr ll;
            if (_loggertype == LoggerType::LOGGER_SYNC)
            {
                ll = tqblog::logger::ptr(new tqblog::SyncLogger(_buildLoggername, _sink, _formatter, _buildLoggerlevel));
                tqblog::Loggermanager::getinstance().addlogger(ll);
                return ll;
            }
            else if (_loggertype == LoggerType::LOGGER_ASYNC)
            {
                ll = tqblog::logger::ptr(new tqblog::AsyncLogger(_buildLoggername, _sink, _formatter, _buildLoggerlevel));
                tqblog::Loggermanager::getinstance().addlogger(ll);
                return ll;
            }
            
        }
    };

};
#endif